package org.myspringframework.core;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ClassPathXmlApplicationContext implements ApplicationContext{
    /**
     * 存储bean的Map集合
     */
    private Map<String,Object> beanMap =new HashMap<>();

    /**
     * 解析myspring.xml文件，创建所有的bean实例，并且将实例放进Map集合中
     * @param resource
     */
    public ClassPathXmlApplicationContext(String resource) {
        try {
            SAXReader reader=new SAXReader();//创建XML解析器
            Document document = reader.read(ClassLoader.getSystemClassLoader().getResourceAsStream(resource));//读取类路径的resources
            List<Node> beanNodes = document.selectNodes("//bean");//选择所有的bean
            for (Node beanNode:beanNodes) {
                Element beanElement = (Element) beanNode;//转换为方法更丰富的Element
                String id = beanElement.attributeValue("id");//获取ID
                String className = beanElement.attributeValue("class");//获取类名
                try {
                    Class<?> clazz = Class.forName(className);//通过反射机制创建对象
                    Object bean = clazz.newInstance();
                    beanMap.put(id,bean);
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
            // 重新遍历集合，这次遍历是为了给Bean的所有属性赋值。重新遍历的目的是解决循环依赖问题
            for (Node beanNode:beanNodes) {
                Element element=(Element) beanNode;
                String id = element.attributeValue("id");//获取bean的Id
                List<Element> property = element.elements("property");//获取全部的属性标签
                for (Element propertyElement:property) {//遍历所有属性
                    try {
                        String name = propertyElement.attributeValue("name");
                        Class<?> type = beanMap.get(id).getClass().getDeclaredField(name).getType();//获取属性类型
                        String setMethodName="set"+name.toUpperCase().charAt(0)+name.substring(1);//获取set方法名
                        Method setMethod = beanMap.get(id).getClass().getDeclaredMethod(setMethodName, type);//获取set方法
                        //获取属性值，属性可能是value，也可能ref
                        String propertyValue= propertyElement.attributeValue("value");//获取value
                        String propertyRef=propertyElement.attributeValue("ref");//获取ref
                        Object propertyVal=null;
                        if (propertyValue!=null){
                            //属性值是简单属性value
                            String simpleName = type.getSimpleName();//获取属性类型的名称
                            switch (simpleName){
                                //将String类型进行类型匹配转换
                                case "byte": case "Byte":
                                    propertyVal=Byte.valueOf(propertyValue);
                                    break;
                                case "short": case "Short":
                                    propertyVal = Short.valueOf(propertyValue);
                                    break;
                                case "int": case "Integer":
                                    propertyVal = Integer.valueOf(propertyValue);
                                    break;
                                case "long": case "Long":
                                    propertyVal = Long.valueOf(propertyValue);
                                    break;
                                case "float": case "Float":
                                    propertyVal = Float.valueOf(propertyValue);
                                    break;
                                case "double": case "Double":
                                    propertyVal = Double.valueOf(propertyValue);
                                    break;
                                case "boolean": case "Boolean":
                                    propertyVal = Boolean.valueOf(propertyValue);
                                    break;
                                case "char": case "Character":
                                    propertyVal = propertyValue.charAt(0);
                                    break;
                                case "String":
                                    propertyVal = propertyValue;
                                    break;
                            }
                            setMethod.invoke(beanMap.get(id),propertyVal);
                        }
                        if (propertyRef!=null){
                            setMethod.invoke(beanMap.get(id),beanMap.get(propertyRef));//invoke方法给set方法设置传入对象和值
                        }
                    } catch (NoSuchFieldException e) {
                        throw new RuntimeException(e);
                    } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Object getBean(String beanId) {
        return beanMap.get(beanId);
    }
}
